package data.platform.common.merge;

import com.fasterxml.jackson.annotation.JsonIgnore;
import data.platform.common.util.DateUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.temporal.TemporalAdjusters;
import java.util.Objects;

@Slf4j
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class MergeTimeWindow {

    // localDateTime millisecond != json covert millisecond
    private long startTime;

    private long endTime;

    @JsonIgnore
    private static final ThreadLocal<SimpleDateFormat> dateFormatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));

    public static MergeTimeWindow getMergeTimeWindow(MergePeriod mergePeriod, LocalDateTime eventTime) {
        switch (mergePeriod) {
            case WEEK:
                return getWeekTimeWindow(eventTime);
            case MONTH:
                return getMonthTimeWindow(eventTime);
            case SEASON:
                return getSeasonTimeWindow(eventTime);
            case YEAR:
                return getYearTimeWindow(eventTime);
            default:
                return getDayTimeWindow(eventTime);
        }
    }

    public MergeTimeWindow(LocalDateTime startTime, LocalDateTime endTime) {
        this(DateUtil.getDateOfLocalDateTime(startTime).getTime(), DateUtil.getDateOfLocalDateTime(endTime).getTime());
    }

    public static MergeTimeWindow getDayTimeWindow(LocalDateTime eventTime) {
        LocalDateTime startTime = eventTime.toLocalDate().atTime(LocalTime.MIN);
        LocalDateTime endTime = eventTime.toLocalDate().atTime(LocalTime.MAX);
        return new MergeTimeWindow(startTime, endTime);
    }

    public static MergeTimeWindow getWeekTimeWindow(LocalDateTime eventTime) {
        LocalDateTime startTime = eventTime.with(DayOfWeek.MONDAY).toLocalDate().atTime(LocalTime.MIN);
        LocalDateTime endTime = eventTime.with(DayOfWeek.SUNDAY).toLocalDate().atTime(LocalTime.MAX);
        return new MergeTimeWindow(startTime, endTime);
    }

    public static MergeTimeWindow getMonthTimeWindow(LocalDateTime eventTime) {
        LocalDateTime startTime = eventTime.with(TemporalAdjusters.firstDayOfMonth()).toLocalDate().atTime(LocalTime.MIN);
        LocalDateTime endTime = eventTime.with(TemporalAdjusters.lastDayOfMonth()).toLocalDate().atTime(LocalTime.MAX);
        return new MergeTimeWindow(startTime, endTime);
    }

    public static MergeTimeWindow getSeasonTimeWindow(LocalDateTime eventTime) {
        int year = eventTime.getYear();
        Month firstMonthOfQuarter = eventTime.getMonth().firstMonthOfQuarter();
        MonthDay firstMonthday = MonthDay.of(firstMonthOfQuarter, 1);
        LocalDateTime startTime = firstMonthday.atYear(year).atTime(LocalTime.MIN);

        Month lastMonthOfQuarter = firstMonthOfQuarter.plus(2);
        MonthDay lastMonthday = MonthDay.of(lastMonthOfQuarter, 1);
        LocalDateTime endTime = lastMonthday.atYear(year).with(TemporalAdjusters.lastDayOfMonth()).atTime(LocalTime.MAX);
        return new MergeTimeWindow(startTime, endTime);
    }

    public static MergeTimeWindow getYearTimeWindow(LocalDateTime eventTime) {
        LocalDateTime startTime = eventTime.with(TemporalAdjusters.firstDayOfYear()).toLocalDate().atTime(LocalTime.MIN);
        LocalDateTime endTime = eventTime.with(TemporalAdjusters.lastDayOfYear()).toLocalDate().atTime(LocalTime.MAX);
        return new MergeTimeWindow(startTime, endTime);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MergeTimeWindow that = (MergeTimeWindow) o;
        return startTime == that.startTime && endTime == that.endTime;
    }

    @Override
    public int hashCode() {
        return Objects.hash(startTime, endTime);
    }

    @Override
    public String toString() {
        return "MergeTimeWindow{" +
                "startTime=" + dateFormatter.get().format(startTime) +
                ", endTime=" + dateFormatter.get().format(endTime) +
                '}';
    }

    public static void main(String[] args) {
        MergeTimeWindow day = getDayTimeWindow(LocalDateTime.now());
        log.info(day.toString());

        MergeTimeWindow week = getWeekTimeWindow(LocalDateTime.now());
        log.info(week.toString());

        MergeTimeWindow month = getMonthTimeWindow(LocalDateTime.now());
        log.info(month.toString());

        MergeTimeWindow season = getSeasonTimeWindow(LocalDateTime.now().plusMonths(1));
        log.info(season.toString());

        MergeTimeWindow year = getYearTimeWindow(LocalDateTime.now());
        log.info(year.toString());
    }
}
